<?php
namespace Twoir\Service\ShowKey;

use Exception;
use GdImage;

/**
 * 验证码
 *
 * cighsen02 365182575
 * time: 2024.05.06
 * Update at: 2025.05.08
*/
class Image implements OutputInterface
{
	// 对象
	private GdImage $image;

	private int $keytog;
	// array 背景色
	private array $bgcolor;
	// array 文字色
	private array $fontcolor;
	// array 干扰色
	private array $distcolor;

	private string $imageType;
	/**
	 * @param string $imageType
	 * @param int $width
	 * @param int $height
	*/
	public function __construct(string $imageType = 'jpeg', int $width = 47, int $height = 20)
	{
		global $public_r;

		if(!extension_loaded('gd')) throw new Exception('请先安装gd库！', 9200);

		$width ??= static::WIDTH;

		$height ??= static::HEIGHT;

		$this->keytog = (int)$public_r['keytog'];
		// 背景色
		$this->bgcolor = isset($public_r['keybgcolor']) && $public_r['keybgcolor'] ? $this->returnRGB($public_r['keybgcolor']) : static::DEFAULT_RGB['keybgcolor'];
		// 文字色
		$this->fontcolor = isset($public_r['keyfontcolor']) && $public_r['keyfontcolor'] ? $this->returnRGB($public_r['keyfontcolor']) : static::DEFAULT_RGB['keyfontcolor'];
		// 干扰色
		$this->distcolor = isset($public_r['keydistcolor']) && $public_r['keydistcolor'] ? $this->returnRGB($public_r['keydistcolor']) : static::DEFAULT_RGB['keydistcolor'];

		$this->imageType = isset($imageType) && array_key_exists($imageType, static::MIME) ? $imageType : 'jpeg';
		// 画布
		$this->image = imagecreate($width, $height);
	}

	/**
	 * 验证码渲染
	 *
	 * @param array $add
	 * @return string
     *
     * @throws Exception
	*/
	public function render(array $add): string
	{
		if (empty($add) || !isset($add['v']) || empty($add['v'])) throw new Exception('参数错误', 9200);

		if (!isset(static::VERIFY_KEY[$add['v']])) throw new Exception('无效' . $add['v'], 9200);

		$key = strtolower($this->randomCharacters());

		ecmsSetShowKey(static::VERIFY_KEY[$add['v']], $key);

		return $this->toBase64DataURI($key);
	}

	/**
	 * 生成图像
	 *
	 * @param string $key
	 * @return string
	*/
	private function toBase64DataURI(string $key): string
	{
	    $callback   = 'image' . ('jpg' === $this->imageType ? 'jpeg' : $this->imageType);
		$bgcolor    = imagecolorallocate($this->image, ...$this->bgcolor);
		imagefill($this->image, 0, 0, $bgcolor);
		$fontcolor = imagecolorallocate($this->image, ...$this->fontcolor);
		imagestring($this->image, 5, 6, 3, $key, $fontcolor);

        // 加入干扰象素
		for($i = 0; $i < 90; $i++) {

			$distcolor = imagecolorallocate($this->image, ...$this->distcolor);
			imagesetpixel($this->image, rand() % 70, rand() % 30, $distcolor);
		}

		ob_start();

        call_user_func($callback, $this->image);
        $imageData = ob_get_contents();
        imagedestroy($this->image);

        ob_end_clean();

		return sprintf('data:image/%s;base64,%s', static::MIME[$this->imageType], base64_encode($imageData));
	}

	/**
	 * 生成随机字符
	 *
	 * @param int $length
	 * @return string
	*/
	public function randomCharacters(int $length = 4): string
	{
		// 字母
		if(1 === $this->keytog) {

			$low_ascii_bound	= 65;
			$upper_ascii_bound	= 90;
			$notuse				= [91];
		}
		// 数字+字母
		elseif(2 === $this->keytog) {

			$low_ascii_bound	= 50;
			$upper_ascii_bound	= 90;
			$notuse				= [58,59,60,61,62,63,64,73,79];
		}
		// 数字
		else {

			$low_ascii_bound	= 48;
			$upper_ascii_bound	= 57;
			$notuse				= [58];
		}

		$string = '';
		$i = 0;
		while($i < $length){

			if(PHP_VERSION < '4.2.0') mt_srand((double)microtime() * 1000000);

			mt_srand();
			$randnum = mt_rand($low_ascii_bound, $upper_ascii_bound);
			if(!in_array($randnum, $notuse)){

				$string .= chr($randnum);
				$i++;
			}
		}

		return $string;
	}

	/**
	 * 把16进制转10进制
	 *
	 * @param string $rgb
	 * @return array
	*/
	private function returnRGB(string $rgb): array
	{
		$rgb = str_replace('#', '', ehtmlspecialchars($rgb));

		return [
			base_convert(substr($rgb, 0, 2), 16, 10),
			base_convert(substr($rgb, 2, 2), 16, 10),
			base_convert(substr($rgb, 4, 2), 16, 10)
		];
	}
}
